#include "MainServer.h"
extern "C" {
#include <libavcodec\avcodec.h>
#include <libavformat\avformat.h>
}


MainServer::MainServer()
	: m_src_pixfmt(AV_PIX_FMT_BGRA)
	, m_dst_pixfmt(AV_PIX_FMT_YUV420P)
{
	m_screen = new Screen();
	m_w = m_screen->getWidth();
	m_h = m_screen->getHeight();
	m_rgb2yuv = new CSwscale(m_src_pixfmt, m_w, m_h, m_dst_pixfmt, m_w, m_h);
	avcodec_register_all();
	av_register_all();
	avformat_network_init();
}


MainServer::~MainServer()
{
	delete m_screen;
	delete m_rgb2yuv;
}

void MainServer::run()
{
	//unsigned char *buff;
	uint8_t *src_data[4];
	int src_linesize[4];
	int ret;
	AVFrame *frame;
	AVCodec *codec;
	AVCodecContext *c;
	FILE *f;
	int got_output;
	codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	f = fopen("test.h264", "wb");
	if (!codec)
	{
		//codec not found
		return;
	}
	c = avcodec_alloc_context3(codec);
	if (!c)
	{
		//Could not allocate video codec context
		return;
	}
	/* put sample parameters */
	//c->bit_rate = 800000;
	/* resolution must be a multiple of two */
	c->width = m_w;
	c->height = m_h;
	/* frames per second */
	c->time_base = { 1, 25 };
	c->gop_size = 10;
	c->max_b_frames = 0;
	c->pix_fmt = AV_PIX_FMT_YUV420P;
	c->qmin = 35;
	c->qmax = 65;
	c->thread_count = 1;
	//set private
	//TODO: use AVDictionary to set param
	if (c->codec_id == AV_CODEC_ID_H264)
	{
		av_opt_set(c->priv_data, "preset", "ultrafast", 0);
		av_opt_set(c->priv_data, "tune", "zerolatency", 0);
	}
	if (c->codec_id == AV_CODEC_ID_HEVC)//h265
	{
		av_opt_set(c->priv_data, "x265-params", "qp=20", 0);
		av_opt_set(c->priv_data, "preset", "ultrafast", 0);
		av_opt_set(c->priv_data, "tune", "zero-latency", 0);
	}
	/* open it */
	if (avcodec_open2(c, codec, NULL) < 0) {
		fprintf(stderr, "Could not open codec\n");
		exit(1);
	}
	ret = av_image_alloc(src_data, src_linesize, m_w, m_h, m_src_pixfmt, 32);
	frame = av_frame_alloc();
	frame->format = c->pix_fmt;
	frame->width = c->width;
	frame->height = c->height;
	ret = av_frame_get_buffer(frame,32);
	ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
	AVPacket pkt;
	int64_t i = 0;      
	av_init_packet(&pkt);
	pkt.data = NULL;    // packet data will be allocated by the encoder
	pkt.size = 0;
	while (true)
	{
		m_screen->update();
		m_screen->getData(src_data[0], src_linesize[0] * m_h);
		m_rgb2yuv->scale(src_data, src_linesize, frame->data, frame->linesize);
		frame->pts = i++;
		ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
		if (ret < 0)
		{
			char errbuf[128];
			av_strerror(ret, errbuf, sizeof(errbuf));
			fprintf(stderr, "Error encoding frame\n");
			break;
		}

		if (got_output)
		{
			printf("Write frame %3d (size=%5d)\n", i, pkt.size);
			fwrite(pkt.data, 1, pkt.size, f);
			av_packet_unref(&pkt);
		}
	}
	fclose(f);
	//saveYUV420((const char **)(frame->data), frame->linesize, m_h);
	av_frame_free(&frame);
	av_freep(&src_data[0]);
}

#include <fstream>
using namespace std;
void MainServer::saveYUV420(const char * srcSlice[], const int srcStride[], int h)
{
	ofstream out;
	out.open("test.yuv", ios::out | ios::binary);
	out.write(srcSlice[0], srcStride[0] * h);
	out.write(srcSlice[1], srcStride[1] * h/2);
	out.write(srcSlice[2], srcStride[2] * h/2);
	out.close();
}